#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/point-to-point-layout-module.h"
#include "ns3/traffic-control-module.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/queue-disc.h"
#include "ns3/object.h"
#include "ns3/traced-value.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/flow-monitor-module.h"
#include <iostream>
#include <iomanip>
#include <map>
#include <string>
#include <fstream>
// Edit by PassLink.cn
using namespace ns3;

std::string dir = "MixTraffic/";
// OutputStreamWrapper 输出文件流
// 平均队列长度 packets T = 1 s
static Ptr<OutputStreamWrapper> AverageQueueLengthStream;
// RTT(ms) X-tag = type
static Ptr<OutputStreamWrapper> QueueRTTStream;
// 吞吐量 (Mb/s) X-tag = type
static Ptr<OutputStreamWrapper> QueueThrougthOutStream;
// 吞吐量 (Mb/s) T = 1 s 
static Ptr<OutputStreamWrapper> CPIE_QueueThrougthOutStream;
// 吞吐量 (Mb/s) T = 1 s 
static Ptr<OutputStreamWrapper> PIE_QueueThrougthOutStream;
// 主动丢包 X-tag = type
static Ptr<OutputStreamWrapper> QueueActiveDropStream;
// 被动丢包 X-tag = type
static Ptr<OutputStreamWrapper> QueuePassiveDropStream;
//平均延迟
static Ptr<OutputStreamWrapper> QueueMeanDelayStream;
//延迟抖动
static Ptr<OutputStreamWrapper> QueueMeanJitterStream;
// 数据记录
//平均队长
static void
AverageQueueLengthTracer(uint32_t oldValue, uint32_t newValue)

{
  *AverageQueueLengthStream->GetStream () << "len: " << newValue << std::endl;
}


static void
CPIE_QueueThrougthOutTracer (uint32_t oldValue, uint32_t newValue)
{
  // 吞吐量
  double Throughput = newValue * 8.0  / 1000000;
  // std::cout <<Simulator::Now ().GetSeconds ()<< "Throughput: " << Throughput << " Mbps" << std::endl;
  *CPIE_QueueThrougthOutStream->GetStream () << "CPIE_QueueThroughput: " << Throughput << std::endl;
}
static void
PIE_QueueThrougthOutTracer (uint32_t oldValue, uint32_t newValue)
{
  // 吞吐量
  double Throughput = newValue * 8.0 / 1000000;
  // std::cout << Simulator::Now ().GetSeconds ()<<"  Throughput: " << Throughput << " Mbps" << std::endl;
  *PIE_QueueThrougthOutStream->GetStream () << "PIE_QueueThroughput: " << Throughput << std::endl;
}


void experiment(std::string queueDiscType)
{

// 是否追踪
bool isTracing = true;



  // 节点数设置为50
  uint32_t nodeLeafNum = 50;
  // TCP拥塞控制算法NewReno
  std::string tcpQueueType = "ns3::TcpNewReno";
  // // 默认队列算法
  // std::string queueDiscType = "ns3::cPieQueueDisc";

  // 路由器Router1和Router2之间的瓶颈链路带宽设置为50Mbps，链路传输时延设置为20ms
  std::string bottleNeckLinkBw = "50Mbps";
  std::string bottleNeckLinkDelay = "20ms";

  // // CMD参数选择队列模式
  // CommandLine cmd(__FILE__);
  // cmd.AddValue("queueDiscType", "Set Queue disc type", queueDiscType);
  // cmd.Parse(argc, argv);

  // 队列类型检查 CODEL DROPTAIL RED PIE CPIE
  if ((queueDiscType != "ns3::RedQueueDisc") 
  && (queueDiscType != "ns3::CoDelQueueDisc") 
  && (queueDiscType != "ns3::DropTailQueue") 
  && (queueDiscType != "ns3::cPieQueueDisc") 
  && (queueDiscType != "ns3::PieQueueDisc"))
  {
    std::cout << "Invalid queue disc type. Use --queueDiscType="
              << "ns3::RedQueueDisc, ns3::CoDelQueueDisc, ns3::DropTailQueue, "
              << "ns3::cPieQueueDisc, or ns3::PieQueueDisc." << std::endl;
    exit(1);
  }

  // RED 主动队列管理
  if (queueDiscType == "ns3::RedQueueDisc")
  {
    Config::SetDefault("ns3::RedQueueDisc::MaxSize",
                       QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, 100)));
    Config::SetDefault("ns3::RedQueueDisc::MinTh", DoubleValue(10.0));
    Config::SetDefault("ns3::RedQueueDisc::MaxTh", DoubleValue(30.0));
    Config::SetDefault("ns3::RedQueueDisc::QW", DoubleValue(0.02));
    Config::SetDefault("ns3::RedQueueDisc::LinkBandwidth", StringValue(bottleNeckLinkBw));
    Config::SetDefault("ns3::RedQueueDisc::LinkDelay", StringValue(bottleNeckLinkDelay));
    Config::SetDefault("ns3::RedQueueDisc::MeanPktSize", UintegerValue(512));
  }
  // CODEL 主动队列管理
  if (queueDiscType == "ns3::CoDelQueueDisc")
  {

    // * ``Interval:``  The default value is 100 ms.
    // * ``Target:`` The default value is 5 ms.
    Config::SetDefault("ns3::CoDelQueueDisc::MaxSize",
                       QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, 100)));
    Config::SetDefault("ns3::CoDelQueueDisc::Interval",
                       TimeValue(Seconds(0.1)));
    Config::SetDefault("ns3::CoDelQueueDisc::Target",
                       TimeValue(Seconds(0.005)));                 
  }
  // DROPTAIL 主动队列管理
  if (queueDiscType == "ns3::DropTailQueue")
  {
    Config::SetDefault ("ns3::DropTailQueue<Packet>::MaxSize",
                      QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, 100)));
  }
  // PIE 主动队列管理
  if (queueDiscType == "ns3::PieQueueDisc")
  {
    // * ``A:`` Value of alpha. The default value is 0.125.
    // * ``B:`` Value of beta. The default value is 1.25.
    Config::SetDefault("ns3::PieQueueDisc::MaxSize",
                       QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, 100)));
    Config::SetDefault("ns3::PieQueueDisc::A", DoubleValue(0.125));
    Config::SetDefault("ns3::PieQueueDisc::B", DoubleValue(1.25));
    Config::SetDefault("ns3::PieQueueDisc::MeanPktSize", UintegerValue(512));
    // 阈值 30ms
    Config::SetDefault("ns3::PieQueueDisc::ActiveThreshold", TimeValue(Seconds(0.05)));
    Config::SetDefault("ns3::PieQueueDisc::QueueDelayReference", TimeValue(Seconds(0.02)));
    Config::SetDefault("ns3::PieQueueDisc::Tupdate", TimeValue(Seconds(0.03)));
  }
  // CPIE 主动队列管理
  if (queueDiscType == "ns3::cPieQueueDisc")
  {
    // * ``A:`` Value of alpha. The default value is 0.125.
    // * ``B:`` Value of beta. The default value is 1.25.
    Config::SetDefault("ns3::cPieQueueDisc::MaxSize",
                       QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, 100)));
    Config::SetDefault("ns3::cPieQueueDisc::A", DoubleValue(0.125));
    Config::SetDefault("ns3::cPieQueueDisc::B", DoubleValue(1.25));
    Config::SetDefault("ns3::cPieQueueDisc::MeanPktSize", UintegerValue(512));
    Config::SetDefault("ns3::cPieQueueDisc::ActiveThreshold", TimeValue(Seconds(0.05)));
    Config::SetDefault("ns3::cPieQueueDisc::QueueDelayReference", TimeValue(Seconds(0.02)));
    Config::SetDefault("ns3::cPieQueueDisc::Tupdate", TimeValue(Seconds(0.03)));
  
  }


  // 创建两个路由器
  NodeContainer routers;
  // 创建两个节点容器，每个容器包含nodeLeafNum个节点
  NodeContainer nodes_send, nodes_res;
  nodes_send.Create(nodeLeafNum);
  routers.Create(2);
  nodes_res.Create(nodeLeafNum);
  
  // 路由器Router1和Router2之间的瓶颈链路带宽设置为50Mbps，链路传输时延设置为20ms
  // 创建路由器之间的点对点链路配置
  PointToPointHelper pointToPointRouter;
  pointToPointRouter.SetDeviceAttribute("DataRate", StringValue(bottleNeckLinkBw));
  pointToPointRouter.SetChannelAttribute("Delay", StringValue(bottleNeckLinkDelay));

  // 端节点到路由器的之间链路带宽设置为100Mbps，链路传输时延设置为5ms，
  // 创建路由器和节点之间的点对点链路配置
  PointToPointHelper pointToPointNode;
  pointToPointNode.SetDeviceAttribute("DataRate", StringValue("100Mbps")); // 假设节点之间的链路带宽为10Mbps
  pointToPointNode.SetChannelAttribute("Delay", StringValue("5ms"));       // 假设节点之间的传输时延为5ms


// 安装网络设备到路由器和端节点
  NetDeviceContainer devices_send[nodeLeafNum], devices_res[nodeLeafNum];
  for (uint32_t i = 0; i < nodeLeafNum; ++i)
    {
       // 连接路由器1和节点1
      devices_send[i] = pointToPointNode.Install(nodes_send.Get(i), routers.Get(0));
       // 连接路由器2和节点2
      devices_res[i] = pointToPointNode.Install(nodes_res.Get(i), routers.Get(1));
    }


  // 连接路由器1和路由器2，创建瓶颈链路
  NetDeviceContainer device_routers;
  device_routers = pointToPointRouter.Install(routers.Get(0), routers.Get(1));


// 结点 安装互联网协议栈
  InternetStackHelper stack;
  stack.Install(routers);
  stack.Install(nodes_send);
  stack.Install(nodes_res);


  // 设置队列管理算法
  TrafficControlHelper tch;
  tch.SetRootQueueDisc(queueDiscType);

  // 获取队列
  Ptr<QueueDisc> qdisc = tch.Install(device_routers.Get(0)).Get(0);
  //  tch.Install(device_routers.Get(1));
 // 设置发送端使用NewReno
  Ptr<Node> senderNode = routers.Get (0);
  senderNode->GetObject<TcpL4Protocol>()->SetAttribute ("SocketType", TypeIdValue (TcpNewReno::GetTypeId ()));

   // 给设备分配IP
  Ipv4AddressHelper address;

  // 路由之间的设备
  address.SetBase("10.1.1.0", "255.255.255.0");
  Ipv4InterfaceContainer interfaces_router = address.Assign(device_routers);;


  Ipv4InterfaceContainer  interfaces_send;
   // address.SetBase("10.1.2.0", "255.255.255.0");
 //路由与结点之间
 for (uint32_t i = 0; i < nodeLeafNum; ++i)
  {
    // 发送端
    ostringstream subset;
    subset<<"10.2."<<(i+1)<<".0";
    address.SetBase(subset.str().c_str(), "255.255.255.0");
    Ipv4InterfaceContainer tempSend = address.Assign(devices_send[i]);
    interfaces_send.Add(tempSend);
  }


  Ipv4InterfaceContainer  interfaces_res;
    // address.SetBase("10.1.3.0", "255.255.255.0");
  //路由与结点之间
 for (uint32_t i = 0; i < nodeLeafNum; ++i)
  {
    // 接收端
    ostringstream subset;
    subset<<"10.3."<<(i+1)<<".0";
    address.SetBase(subset.str().c_str(), "255.255.255.0");
    Ipv4InterfaceContainer tempRes = address.Assign(devices_res[i]);
    interfaces_res.Add(tempRes);
   
  }

  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

// 发送端
//使用ON/OFF模型分组产生器OnOffApplication 进行发送数据包，发包速率为 100Mbps。
  OnOffHelper sendOnOffHelper("ns3::TcpSocketFactory", Address());
  sendOnOffHelper.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
  sendOnOffHelper.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
  sendOnOffHelper.SetAttribute ("PacketSize", UintegerValue (100));
  sendOnOffHelper.SetAttribute ("DataRate", StringValue ("100Mbps")); //bit/s

// 安装应用

// 接收端
ApplicationContainer receiveApps;
Address sinkLocalAddress(InetSocketAddress(Ipv4Address::GetAny(), 5001));
PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory", sinkLocalAddress);
// (为每个接收节点安装PacketSink)
for (uint32_t i = 0; i < nodes_res.GetN(); ++i) {
    receiveApps.Add(packetSinkHelper.Install(nodes_res.Get(i)));
}

receiveApps.Start(Seconds(0.0));
receiveApps.Stop(Seconds(10.0));

// isTracing = false;
// 记录数据
if (isTracing)
    {
      
      qdisc->TraceConnectWithoutContext("PacketsInQueue",MakeCallback(&AverageQueueLengthTracer));
     
      // 时间为横轴
      if(queueDiscType == "ns3::cPieQueueDisc"){
        qdisc->TraceConnectWithoutContext("BytesInQueue",MakeCallback(&CPIE_QueueThrougthOutTracer));
                   // 发送端
          ApplicationContainer sendApps;
          // 为每个发送节点安装OnOffApplication，并将目标地址设置为接收节点的IP和端口
          for (uint32_t i = 0; i < nodes_send.GetN(); ++i) {
              if(nodes_send.GetN() > nodeLeafNum - 5){
                  sendOnOffHelper.SetAttribute ("DataRate", StringValue ("130Mbps")); //bit/s
              }
              AddressValue remoteAddress(InetSocketAddress(interfaces_res.GetAddress(i, 0), 5001));
              sendOnOffHelper.SetAttribute("Remote", remoteAddress);
              sendApps.Add(sendOnOffHelper.Install(nodes_send.Get(i)));
          }
         
          sendApps.Start(Seconds(1.0));
          sendApps.Stop(Seconds(9.0));
      }
     else if(queueDiscType == "ns3::PieQueueDisc"){
         qdisc->TraceConnectWithoutContext("BytesInQueue",MakeCallback(&PIE_QueueThrougthOutTracer));
                    // 发送端
          ApplicationContainer sendApps;
          // 为每个发送节点安装OnOffApplication，并将目标地址设置为接收节点的IP和端口
          for (uint32_t i = 0; i < nodes_send.GetN(); ++i) {
            if(nodes_send.GetN() > nodeLeafNum - 5){
                  sendOnOffHelper.SetAttribute ("DataRate", StringValue ("130Mbps")); //bit/s
              }
              AddressValue remoteAddress(InetSocketAddress(interfaces_res.GetAddress(i, 0), 5001));
              sendOnOffHelper.SetAttribute("Remote", remoteAddress);
              sendApps.Add(sendOnOffHelper.Install(nodes_send.Get(i)));
          }

          sendApps.Start(Seconds(1.0));
          sendApps.Stop(Seconds(9.0));
      }
      else{
            // 发送端
          ApplicationContainer sendApps;
          // 为每个发送节点安装OnOffApplication，并将目标地址设置为接收节点的IP和端口
          for (uint32_t i = 0; i < nodes_send.GetN(); ++i) {

              AddressValue remoteAddress(InetSocketAddress(interfaces_res.GetAddress(i, 0), 5001));
              sendOnOffHelper.SetAttribute("Remote", remoteAddress);
              sendApps.Add(sendOnOffHelper.Install(nodes_send.Get(i)));
          }
          sendApps.Start(Seconds(1.0));
          sendApps.Stop(Seconds(9.0));
      }


    }

// 流量分析
FlowMonitorHelper flowmon;
// Ptr<FlowMonitor> monitor = flowmon.Install(routers.Get(0));
Ptr<FlowMonitor> monitor = flowmon.InstallAll();

// 仿真时间设置为30s
Simulator::Stop(Seconds(10.0));
Simulator::Run();

if(isTracing){
  // 流量汇总
  std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats();

  double Throughput = stats[1].rxBytes * 8.0 / (stats[1].timeLastRxPacket.GetSeconds () - stats[1].timeFirstRxPacket.GetSeconds ()) / 1000000;
   // 吞吐量
  *QueueThrougthOutStream->GetStream () << queueDiscType <<"Throughput: " << Throughput << " Mbps"<<std::endl;
  // 平均延迟
  *QueueMeanDelayStream->GetStream () << queueDiscType <<  "MeanDelay: " << stats[1].delaySum.GetSeconds () / stats[1].rxPackets << std::endl;
  // 延迟抖动
  *QueueMeanJitterStream->GetStream () << queueDiscType << "MeanJitter: " << stats[1].jitterSum.GetSeconds () / (stats[1].rxPackets - 1) << std::endl;

  // Ipv4FlowProbe::DROP_QUEUE  被动
  uint32_t packetsDroppedByNetDevice = 0;
if (stats[1].packetsDropped.size () > Ipv4FlowProbe::DROP_QUEUE)
    {
      packetsDroppedByNetDevice = stats[1].packetsDropped[Ipv4FlowProbe::DROP_QUEUE];
    }

  // std::cout << "Packets Dropped by NetDevice:" << packetsDroppedByNetDevice<< std::endl;
  *QueuePassiveDropStream->GetStream () << queueDiscType <<"PacketsDroppedByNetDevice: " << packetsDroppedByNetDevice << std::endl;


  // Ipv4FlowProbe::DROP_QUEUE_DISC 主动丢包数
  uint32_t packetsDroppedByQueueDisc = 0;
  if (stats[1].packetsDropped.size () > Ipv4FlowProbe::DROP_QUEUE_DISC)
    {
      packetsDroppedByQueueDisc = stats[1].packetsDropped[Ipv4FlowProbe::DROP_QUEUE_DISC];
    }
  // std::cout << "Packets Dropped by Queue Disc:" << packetsDroppedByQueueDisc << std::endl;
  *QueueActiveDropStream->GetStream () << queueDiscType << "PacketsDroppedByQueueDisc:" << packetsDroppedByQueueDisc << std::endl;

}
 
Simulator::Destroy();

}

int main (int argc, char **argv)
{

// 文本创建
  AsciiTraceHelper asciiTraceHelper;
  QueueThrougthOutStream= asciiTraceHelper.CreateFileStream ((dir+"QueueThrougthOut.txt").c_str ());
  QueueActiveDropStream= asciiTraceHelper.CreateFileStream ((dir+"QueueActiveDrop.txt").c_str ());
  QueuePassiveDropStream= asciiTraceHelper.CreateFileStream ((dir+"QueuePassiveDrop.txt").c_str ());
  CPIE_QueueThrougthOutStream= asciiTraceHelper.CreateFileStream ((dir+"CPIE_QueueThrougthOut.txt").c_str ());
  PIE_QueueThrougthOutStream= asciiTraceHelper.CreateFileStream ((dir+"PIE_QueueThrougthOut.txt").c_str ());
  QueueMeanDelayStream= asciiTraceHelper.CreateFileStream ((dir+"QueueMeanDelay.txt").c_str ());
  QueueMeanJitterStream= asciiTraceHelper.CreateFileStream ((dir+"QueueMeanJitter.txt").c_str ());


  std::cout << "Simulation with RedQueueDisc: Start\n" << std::flush;
  // QueueRTTStream = asciiTraceHelper.CreateFileStream ((dir+"red-rtt.txt").c_str ());
  AverageQueueLengthStream = asciiTraceHelper.CreateFileStream ((dir+"redAverageQueueLength.txt").c_str ());
  experiment ("ns3::RedQueueDisc");
  std::cout << "Simulation with RedQueueDisc: End\n" << std::flush;
  std::cout << "------------------------------------------------\n";


  std::cout << "Simulation with CoDelQueueDisc: Start\n" << std::flush;
  // QueueRTTStream = asciiTraceHelper.CreateFileStream ((dir+"codel-rtt.txt").c_str ());
    AverageQueueLengthStream = asciiTraceHelper.CreateFileStream ((dir+"codelAverageQueueLength.txt").c_str ());
  experiment ("ns3::CoDelQueueDisc");
  std::cout << "Simulation with CoDelQueueDisc: End\n" << std::flush;
  std::cout << "------------------------------------------------\n";

  // std::cout << "Simulation with DropTailQueue: Start\n" << std::flush;
    // QueueRTTStream = asciiTraceHelper.CreateFileStream ((dir+"droptail-rtt.txt").c_str ());
    //   AverageQueueLengthStream = asciiTraceHelper.CreateFileStream ((dir+"-aql.txt").c_str ());
  // experiment ("ns3::DropTailQueue");
  // std::cout << "Simulation with DropTailQueue: End\n" << std::flush;
  // std::cout << "------------------------------------------------\n";

  std::cout << "Simulation with cPieQueueDisc: Start\n" << std::flush;
    // QueueRTTStream = asciiTraceHelper.CreateFileStream ((dir+"cpie-rtt.txt").c_str ());
      AverageQueueLengthStream = asciiTraceHelper.CreateFileStream ((dir+"cpieAverageQueueLength.txt").c_str ());
  experiment ("ns3::cPieQueueDisc");
  std::cout << "Simulation with cPieQueueDisc: End\n" << std::flush;
  std::cout << "------------------------------------------------\n";

  std::cout << "Simulation with PieQueueDisc: Start\n";
  // QueueRTTStream = asciiTraceHelper.CreateFileStream ((dir+"pie-rtt.txt").c_str ());
  AverageQueueLengthStream = asciiTraceHelper.CreateFileStream ((dir+"pieAverageQueueLength.txt").c_str ());
  experiment ("ns3::PieQueueDisc");
  std::cout << "Simulation with PieQueueDisc: End\n";
  return 0;
}


